package com.ycl.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ycl.common.*;
import com.ycl.pojo.BlackList;
import com.ycl.pojo.ChatRecord;
import com.ycl.pojo.User;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.regex.Pattern;

//客户端
public class ClientController {
    private Socket socket;
    //1 表示首页菜单  2表示系统菜单  -1 表示私聊   -2 表示群聊
    private int menuType = 1;
    // 输入
    private Scanner input = new Scanner(System.in);
    //我的账户
    private String me;
    //发送到的账户
    private String to;
    //登录次数使用
    private int num = 0;
    //锁对象，防止线程不按顺序
    private static Object sync = new Object();
    // 本地线程，存储用户信息
    private ThreadLocal<String> threadLocal = new ThreadLocal<>();
    // 网址
    String host = YChatConstant.Host;
    // 端口
    int port = YChatConstant.Port;


    public static void main(String[] args) throws Exception {
        // 开始图标
        Icon.loginIcon(6);
        // 加载进度调
        Icon.progressBar();
        //初始化客户端 与服务端连接
        new ClientController().init();
    }

    /**
     * 初始化，与服务器连接
     *
     * @throws Exception
     */
    public void init() throws Exception {
        try {
            socket = new Socket(host, port);
            listenInfoFromServer();
            chooseMenuType();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 锁住当前对象，保证数据一致性
     *
     * @param t
     * @throws Exception
     */
    private void lockOrReleaseObject(boolean t) throws Exception {
        synchronized (sync) {
            if (t) {          //锁住
                sync.wait();
            } else {         //释放
                sync.notifyAll();
            }
        }
    }

    /**
     * 读取服务端消息
     */
    private void listenInfoFromServer() {
        new Thread(() -> {
            while (true) {
                try {
                    InputStream inputStream = socket.getInputStream();
                    byte[] b = new byte[1024 * 1024];
                    DataInputStream dataInputStream = new DataInputStream(inputStream);
                    dataInputStream.read(b);
                    //监听服务端返回消息
                    String info = new String(b, StandardCharsets.UTF_8);
                    serverMessageHandle(JSON.parseObject(info));
                } catch (Exception e) {
                    e.printStackTrace();
                    break;
                }
            }
        }).start();
    }

    /**
     * 首页页面
     */
    public void homeMenu() {
        System.out.println("==================================主页=================================");
        System.out.println("||                               YChat                              || ");
        System.out.println("||                                                                  || ");
        System.out.println("|| \t1.注册\t||\t2.登录\t||\t3.忘记密码\t||\t4.退出\t||\t0.支持我们\t||");
        System.out.println("||                                                                  || ");
        System.out.println("======================================================================");
        System.out.println();
    }

    /**
     * 用户页面
     *
     * @param name
     */
    public void userMenu(String name) {
        System.out.print("====================================");
        if (name == null) name = "";
        for (int i = 0; i < (10 - name.length()) / 2; i++)
            System.out.print(" ");
        System.out.print(name);
        for (int i = 0; i < (10 - name.length()) / 2; i++)
            System.out.print(" ");
        System.out.println("=====================================");
        System.out.println("||                                                                              || ");
        System.out.println("||                                                                              || ");
        System.out.println("||\t5.私聊\t||\t6.群聊\t||\t7.在线名单\t||\t8.修改密码\t||\t9.注销\t||\t10.退出\t||");
        System.out.println("||                                                                              || ");
        System.out.println("|| *高级功能(VIP) ||\t11.查询历史记录\t||\t12.拉黑\t||\t13.取消拉黑\t||\t14.VIP办理  ||");
        System.out.println("||                  15.拉黑列表                                                  ||");
        System.out.println("||                                                                              || ");
        System.out.println("==================================================================================");
        System.out.println();
    }

    /**
     * TODO
     * 逻辑代码 客户端运行主体
     *
     * @throws Exception
     */
    private void chooseMenuType() throws Exception {
        while (true) switch (menuType) {
            //一级菜单
            case 1 -> {
                //首页菜单
                homeMenu();
                String level = input.nextLine();
                switch (level) {
                    case "1" ->
                        //注册
                            register();
                    case "2" ->
                        //登录
                            login();
                    case "3" ->
                        // 忘记密码
                            forgetPassword();
                    case "4" ->
                        //退出系统
                            exitHomePage();
                    case "0" ->
                        //小组
                            group();
                }
            }
            //二级菜单
            case 2 -> {
                //系统菜单
                userMenu(me);
                String level = input.nextLine();
                switch (level) {
                    case "5" ->
                        // 私聊
                            privateChat();
                    case "6" ->
                        //群聊
                            sendType("from", "6");
                    case "7" ->
                        // 在线人数
                            sendType("text", "7");
                    case "8" ->
                        // 修改密码
                            changePassword();
                    case "9" ->
                        // 注销账号
                            sendType("text", "9");
                    case "10" ->
                        // 退出
                            exitUserPage();
                    case "11" ->
                        // 历史消息
                            historyMessage();
                    case "12" ->
                        // 拉黑
                            intoBlackList();
                    case "13" ->
                        // 取消拉黑
                            outFoBlackList();
                    case "14" ->
                        // VIP办理
                            VIPHandle();
                    case "15" ->
                        // 拉黑列表
                            sendType("from", "15");
                }
            }
            case -1 ->
                //私聊模式
                    privateChatMain();
            case -2 ->
                //群聊模式
                    groupChatMain();
        }
    }

    /**
     * 6 7 9 15 发送功能类型
     *
     * @param from
     * @param number
     * @throws Exception
     */
    private void sendType(String from, String number) throws Exception {
        JSONObject m = new JSONObject();
        m.put(from, me);
        m.put("type", number);
        ServerController.sendInfo(socket, m);
        lockOrReleaseObject(true);
    }

    /**2
     *
     * 14 VIP办理
     *
     * @throws Exception
     */
    private void VIPHandle() throws Exception {
        System.out.println("确定办理VIP吗?:(yes/no)");
        String to = input.nextLine();
        if (to.contains("yes")) {
            JSONObject m = new JSONObject();
            m.put("from", me);
            m.put("to", to);
            m.put("type", "14");
            ServerController.sendInfo(socket, m);
            lockOrReleaseObject(true);
        }
    }

    /**
     * 13 取消黑名单
     *
     * @throws Exception
     */
    private void outFoBlackList() throws Exception {
        System.out.println("输入用户昵称，将对方从你的黑名单移除:");
        String to = input.nextLine();
        JSONObject m = new JSONObject();
        m.put("from", me);
        m.put("to", to);
        m.put("type", "13");
        ServerController.sendInfo(socket, m);
        lockOrReleaseObject(true);
    }

    /**
     * 12 加入黑名单
     *
     * @throws Exception
     */
    private void intoBlackList() throws Exception {
        System.out.println("想拉黑谁？请输入该用户的用户名:");
        String to = input.nextLine();
        JSONObject m = new JSONObject();
        m.put("from", me);
        m.put("to", to);
        m.put("type", "12");
        ServerController.sendInfo(socket, m);
        lockOrReleaseObject(true);
    }

    /**
     * 11 历史消息记录
     *
     * @throws Exception
     */
    private void historyMessage() throws Exception {
        System.out.println("想要查询与谁的聊天记录？");
        String to = input.nextLine();
        JSONObject m = new JSONObject();
        m.put("from", me);
        m.put("to", to);
        m.put("type", "11");
        ServerController.sendInfo(socket, m);
        lockOrReleaseObject(true);
    }

    /**
     * 10 退出用户界面
     *
     * @throws Exception
     */
    private void exitUserPage() throws Exception {
        //发送消息到服务端
        JSONObject m = new JSONObject();
        m.put("text", me);
        m.put("type", "10");
        ServerController.sendInfo(socket, m);
        //退出系统
        try {
            socket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.exit(0);
    }

    /**
     * 8 修改密码
     *
     * @throws Exception
     */
    private void changePassword() throws Exception {
        User user = new User();
        System.out.println("请输入旧密码：");
        String oldPas = input.nextLine();
        user.setUsername(me);
        System.out.println("请输入新密码：");
        String newPas = input.nextLine();
        //拼接密码字段，后面解析用
        user.setPassword(oldPas + "_" + newPas);
        JSONObject m = new JSONObject();
        m.put("text", user);
        m.put("type", "8");
        ServerController.sendInfo(socket, m);
        lockOrReleaseObject(true);
        menuType = 2;
    }

    /**
     * 5 私聊
     *
     * @throws Exception
     */
    private void privateChat() throws Exception {
        System.out.println("请输入想要私聊的用户名称：");
        to = input.nextLine();
        //发送消息到服务端
        JSONObject m = new JSONObject();
        m.put("from", me);
        m.put("to", to);
        m.put("type", "5");
        ServerController.sendInfo(socket, m);
        lockOrReleaseObject(true);
    }

    /**
     * 4 退出主页
     */
    private void exitHomePage() {
        try {
            //关闭连接
            input.close();
            socket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        //退出
        System.exit(0);
    }

    /**
     * 3 忘记密码
     *
     * @throws Exception
     */
    private void forgetPassword() throws Exception {
        User user = new User();
        System.out.println("输入用户名：");
        String s = input.nextLine();
        user.setUsername(s);
        String email;
        System.out.println("请输入用户邮箱：");
        email = input.nextLine();
        user.setEmail(email);
        JSONObject m = new JSONObject();
        m.put("text", user);
        m.put("type", "3");
        //发送消息到服务端
        ServerController.sendInfo(socket, m);
        lockOrReleaseObject(true);
    }

    /**
     * 2 登录
     *
     * @throws Exception
     */
    private void login() throws Exception {
        while (true) {
            //登录成功，跳出循环
            if (num < 0) {
                num = 0;  //重新初始化
                break;
            }
            if (num > 2) {
                //大于三次，退出系统
                System.out.println("3次输入错误，即将退出系统");
                //退出系统
                System.exit(0);
                break;
            }
            if (num > 0) {
                System.out.println("请重新输入用户名和密码");
            }
            User user = new User();
            System.out.println("输入用户名：");
            String s = input.nextLine();
            // 将用户名存入当前线程
            threadLocal.set(s);
            user.setUsername(s);
            System.out.println("输入密码：");
            s = input.nextLine();
            user.setPassword(s);
            JSONObject m = new JSONObject();
            m.put("text", user);
            m.put("type", "2");
            //发送消息到服务端
            ServerController.sendInfo(socket, m);
            lockOrReleaseObject(true);
        }
    }

    /**
     * 1 注册
     *
     * @throws Exception
     */
    private void register() throws Exception {
        User user = new User();
        System.out.println("输入用户名：");
        String s = input.nextLine();
        user.setUsername(s);
        System.out.println("请输入用户密码：");
        String password = input.nextLine();
        user.setPassword(password);
//                        String email;
//                        int eCount = 0;
//                        System.out.println("请输入用户邮箱：");
//                        email = input.nextLine();
//                        user.setEmail(email);
        String email;
        int eCount = 0;
        do {
            eCount++;
            if (eCount == 4) {
                System.exit(0);
            }
            if (eCount > 1) {
                System.out.println("格式错误，请重新输入用户邮箱：");
                email = input.nextLine();
            } else {
                System.out.println("请输入用户邮箱(请用真实邮箱,如QQ邮箱)：");
                email = input.nextLine();
            }
        } while (!Pattern.matches("[a-zA-Z0-9]+@[a-zA-Z0-9]+\\.[a-zA-Z0-9]+", email));
        user.setEmail(email);

        // 发送QQ邮箱验证码
        // 生成验证码
        String check = Check.createCheck();
        // 发送QQ邮箱
        System.out.println("验证码已发送到您的邮箱！");
        new Email().sendEmail(email, check);
        Thread.sleep(1000);

        System.out.println("请输入验证码:");
        String userCheck = input.nextLine();
        if (!check.equals(userCheck)) {
            System.out.println("验证码错误");
        } else {
            //发送消息到服务端
            JSONObject m = new JSONObject();
            m.put("text", user);
            m.put("type", "1");
            //发送到服务端
            ServerController.sendInfo(socket, m);
            //锁住当前对象，保证数据一致
            lockOrReleaseObject(true);
        }
    }

    /**
     * 0 支持我们
     */
    private void group(){
        System.out.println("================== ==  YChat  == ==================");
        System.out.println("||                                               \t||");
        System.out.println("|| 小组组长：严成龙                                 \t||");
        System.out.println("||                                               \t||");
        System.out.println("|| 小组成员：马梦欣、邓文杰、姚梦绮、张贝、徐信怡、张航   \t||");
        System.out.println("||                                               \t||");
        System.out.println("|| 简介:YChat从确定到做什么,到想项目名,再到构思整体架    \t||");
        System.out.println("||     构分配任务,然后一鼓作气写模块功能、改模块功能、   \t||");
        System.out.println("||     测试模块、改BUG…… 最后完成.......✌            \t||");
        System.out.println("||                                               \t||");
        System.out.println("||                湖北师范大学 2109                \t||");
        System.out.println("||                                               \t||");
        System.out.println("================== ==  YChat  == ==================");

        try {
            Thread.sleep(1000);
            System.out.println();
            System.out.println("👉👉👉👉👉👉👉👉👉👉👉👉👉👉👉👉👉");
            System.out.println("扫中间二维码进行打赏!");
            System.out.println("快来支持我们吧！");
            Thread.sleep(1000);
            QRCodePay.QRCode();
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * -1 处理私聊模式
     *
     * @throws Exception
     */
    private void privateChatMain() throws Exception {
        System.out.println("欢迎进入私聊模式，输入quit即可退出聊天模式！");
        while (true) {
            String content = input.nextLine();
            //退出聊天标志 quit
            if ("quit".equals(content)) {
                menuType = 2;       //切换到二级菜单
                break;
            }
            JSONObject m = new JSONObject();
            m.put("from", me);
            m.put("to", to);
            m.put("text", content);
            m.put("type", "-1");
            ServerController.sendInfo(socket, m);
        }
    }

    /**
     * -2 处理群聊模式
     *
     * @throws Exception
     */
    private void groupChatMain() throws Exception {
        System.out.println("欢迎进入群聊模式，输入quit即可退出聊天模式！");
        while (true) {
            String content = input.nextLine();
            //退出聊天标志 quit
            if ("quit".equals(content)) {
                menuType = 2;       //切换到二级菜单
                break;
            }
            //发送消息
            JSONObject m = new JSONObject();
            m.put("from", me);
            m.put("text", content);
            m.put("type", "-2");
            ServerController.sendInfo(socket, m);
        }
    }


//    *********************************************************************************************    //
    /**
     * 监听服务端返回的消息
     * 并进行相应处理
     *
     * @param msg
     * @throws Exception
     */
    private void serverMessageHandle(JSONObject msg) throws Exception {
        //服务端返回消息后，awake(); 释放锁，让下一个对象获得锁
        switch (Integer.parseInt(msg.getString("type"))) {
            case 2 ->
                // 根据服务器返回的信息进行登录
                    handlerLogin(msg);
            case 5 ->
                //根据服务器返回的信息进行私聊
                    handlerPrivateChat(msg);
            case 6 ->
                //根据服务器返回的信息进行开启群聊
                    handleGroupChat();
            case 8 ->
                //根据服务器返回的信息进行修改密码
                    handlerChangePassword(msg);
            case 9 ->
                // 根据服务器返回的信息进行注销账户
                    unsubscribe();
            case 11 ->
                // 根据服务器返回的信息进行输出历史记录信息
                    handlerInquireHistory(msg);
            case 12 ->
                // 根据服务器返回的信息进行输出拉黑信息
                    handlerBlackList(msg);
            case 13 ->
                // 根据服务器返回的信息进行输出取消拉黑信息
                    handlerBlackList(msg);
            case 14 ->
                // 根据服务器返回的信息进行输出
                    handlerVIP(msg);
            case 15 ->
                // 根据服务器返回的信息进行输出取消拉黑信息
                    handlerInputBlackList(msg);
            case -1 ->
                //根据服务器返回的信息进行私聊模式
                    handlerPrivateChatMain(msg);
            case -2 ->
                //根据服务器返回的信息进行群聊模式
                    handlerGroupChatMain(msg);
            default ->
                //默认直接输出服务器返回的信息
                    handlerDefault(msg);
        }
    }



    private void handlerDefault(JSONObject msg) throws Exception {
        System.out.println(msg.getString("text"));
        lockOrReleaseObject(false);
    }

    /**
     * -2.1 据服务器返回的信息进行群聊模式
     *
     * @param msg
     */
    private void handlerGroupChatMain(JSONObject msg) {
        if (menuType != 4) {  //对方不处于私聊模式时
            System.out.println("你有一条新消息，按任意键可以进入群聊模式！");
            to = msg.getString("from");
        }
        System.out.println(msg.getString("from") + ":" + msg.getString("text"));
        //变成群聊模式
        menuType = -2;
    }

    /**
     * -1.1 根据服务器返回的信息进行私聊模式
     *
     * @param msg
     */
    private void handlerPrivateChatMain(JSONObject msg) {
        if (menuType != 3) {        //对方不处于私聊模式时
            System.out.println("你有一条新消息，按任意键可以进入聊天模式！");
            to = msg.getString("from");
        }
        //输出消息
        System.out.println(msg.getString("from") + ":" + msg.getString("text"));
        menuType = -1;
    }

    /**
     * 15.2 根据服务器返回的信息进行输出取消拉黑信息
     *
     * @param msg
     * @throws Exception
     */
    private void handlerInputBlackList(JSONObject msg) throws Exception {
        menuType = 2;
        String s = (String) msg.get("text");
        List list = JSONObject.parseObject(s, List.class);
        //System.out.println(s);
        if (list == null || list.isEmpty()) {
            System.out.println("您还没有拉黑任何人！可以尝试拉黑讨厌的人哦！");
        } else {
            System.out.println("拉黑列表如下 👇");
            for (Object o : list) {
                BlackList blackList = JSONObject.parseObject(o.toString(), BlackList.class);
                System.out.println("👉:" + blackList.getBan());
            }
        }
        System.out.println("=============================================");
        lockOrReleaseObject(false);
    }

    private void handlerVIP(JSONObject msg) throws Exception {
        String string = msg.getString("text");
        if (string.contains("办理成功"))
            QRCodePay.QRCode();
        System.out.println(string);
        lockOrReleaseObject(false);
    }

    /**
     * 12.2 13.2 根据服务器返回的信息进行输出拉黑及取消拉黑信息
     *
     * @param msg
     * @throws Exception
     */
    private void handlerBlackList(JSONObject msg) throws Exception {
        String context = msg.getString("text");
        menuType = 2;
        System.out.println(context);
        lockOrReleaseObject(false);
    }

    /**
     * 11.2 根据服务器返回的信息进行输出历史记录信息
     *
     * @param msg
     * @throws Exception
     */
    private void handlerInquireHistory(JSONObject msg) throws Exception {
        System.out.println("查询成功！");
        menuType = 2;
        List list = JSONObject.parseObject(msg.get("text").toString(), List.class);
        System.out.println("👇=============================================👇");
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        if (list == null) {
            System.out.println("你与他(她)之间没有消息，快和他(她)聊天吧！");
        } else {
            for (Object str : list) {
                ChatRecord chatRecord = JSONObject.parseObject(str.toString(), ChatRecord.class);
                System.out.println(formatter.format(chatRecord.getTime()));
                if (chatRecord.getSender().equals(me)) {
                    System.out.println(chatRecord.getText() + "<--" + chatRecord.getSender());
                } else {
                    System.out.println(chatRecord.getSender() + "-->" + chatRecord.getText());
                }
            }
        }
        System.out.println("👆=============================================👆");
        lockOrReleaseObject(false);
    }

    /**
     * 9.2 根据服务器返回的信息进行注销账户
     *
     * @throws Exception
     */
    private void unsubscribe() throws Exception {
        System.out.println("注销成功");
        menuType = 1;       //到一级菜单
        lockOrReleaseObject(false);
    }


    /**
     * 8.2 根据服务器返回的信息进行修改密码
     *
     * @param msg
     * @throws Exception
     */
    private void handlerChangePassword(JSONObject msg) throws Exception {
        String context = msg.getString("text");
        if ("ok".equals(context)) {
            System.out.println("修改成功");
            menuType = 1;       //到一级菜单
        } else if ("error".equals(context)) {
            System.out.println("原密码错误!");
            menuType = 2;       //到二级级菜单
        } else {
            System.out.println("修改失败，请重试");
            menuType = 2;       //到二级级菜单
        }
        lockOrReleaseObject(false);
    }

    /**
     * 6.2 根据服务器返回的信息进行群聊
     *
     * @throws Exception
     */
    private void handleGroupChat() throws Exception {
        menuType = -2;
        lockOrReleaseObject(false);
    }

    /**
     * 5.2 根据服务器返回的信息进行私聊
     *
     * @param msg
     * @throws Exception
     */
    private void handlerPrivateChat(JSONObject msg) throws Exception {
        //用户不在线或者没有该用户
        if (msg.getString("text").length() > 1) {
            System.out.println(msg.getString("text"));
            //系统菜单
            menuType = 2;
        } else {
            //开启私聊
            menuType = -1;
            to = msg.getString("to");
        }
        lockOrReleaseObject(false);
    }

    /**
     * 2.2 根据服务器返回的信息进行登录
     *
     * @param msg
     * @throws Exception
     */
    private void handlerLogin(JSONObject msg) throws Exception {
        //登录成功,跳到二级菜单
        if ("yes".equals(msg.getString("text"))) {
            me = msg.getString("user");
            //跳转系统菜单，所以这里是2
            menuType = 2;
            //登录成功，跳出循环用
            num = -1;
        } else if (msg.getString("text").contains("用户已登录")) {
            me = msg.getString("user");
            menuType = 2;
            num = -1;
            System.out.println(msg.getString("text"));
        } else {
            //登录失败，次数+1
            num += 1;
            //输出错误原因
            System.out.println(msg.getString("text"));
            menuType = 1;
        }
        lockOrReleaseObject(false);
    }
}
